﻿using System.Linq;
using System.Reflection;
using Caliburn.Micro;
using Castle.Facilities.TypedFactory;
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.Resolvers.SpecializedResolvers;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using FluentValidation;
using NHibernate;
using TimeKeeper.Infrastructure;

namespace TimeKeeper
{
    public class ContainerInstaller : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel, true));
            
            container.AddFacility<TypedFactoryFacility>();

            IEntityFactory entityFactory = new EntityFactory(container);

            var configure = Fluently.Configure().ExposeConfiguration(x => x.SetInterceptor(new CustomInterceptor(entityFactory)))
                .Database(SQLiteConfiguration.Standard.UsingFile("data.sqlite"))
                .Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly()));

            var sessionFactory = configure.BuildSessionFactory();

            container.Register(
                Component.For<IWindsorContainer>().Instance(container),

                Component.For<IEventAggregator>()
                         .ImplementedBy<EventAggregator>()
                         .LifeStyle.Singleton,
                Component.For<IWindowManager>()
                         .ImplementedBy<WindowManager>()
                         .LifeStyle.Singleton,
                Component.For<IEntityFactory>().Instance(entityFactory),

                Component.For<ISessionFactory>().Instance(sessionFactory).LifeStyle.Singleton,
                Component.For<ISession>().UsingFactoryMethod(sessionFactory.OpenSession).LifeStyle.Transient,
                Component.For<IStatelessSession>().UsingFactoryMethod(sessionFactory.OpenStatelessSession).LifeStyle.Singleton
            );

            //Register validators
            foreach (var type in Assembly.GetExecutingAssembly().GetTypes().Where(x => x.BaseType != null && x.BaseType.IsGenericType && x.BaseType.GetGenericTypeDefinition() == typeof(AbstractValidator<>)))
            {
                var validatedType = type.BaseType.GetGenericArguments().First();
                container.Register(
                    Component.For(typeof(IValidator<>).MakeGenericType(validatedType))
                        .ImplementedBy(type)
                        .LifeStyle.Singleton
                    );
            }

            //Register ViewModels automatically with event aggregator subscription
            container.Register(
                AllTypes.FromThisAssembly().Where(x => x.FullName != null && x.FullName.EndsWith("ViewModel"))
                        .Configure(x => x.LifeStyle.Transient
                            .OnCreate((kernel, thisType) =>
                            {
                                if (thisType is IHandle)
                                {
                                    kernel.Resolve<IEventAggregator>().Subscribe(thisType);
                                }
                            })
                        )
            );

            //Register ViewModel factories
            foreach (var type in Assembly.GetExecutingAssembly().GetTypes().Where(x => x.FullName != null && x.FullName.EndsWith("ViewModel")))
            {
                container.Register(Component.For(typeof(IViewModelFactory<>).MakeGenericType(type)).AsFactory());
            }
        }
    }
}
